package no.ntnu.idi.deid.statistical.tfidf;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.Map.Entry;
import no.ntnu.idi.deid.documentunits.IDocumentUnits;
import no.ntnu.idi.deid.preprocessor.Documents;

public abstract  class DocumentFrequency  {
	
	protected TreeMap<String, Double> idfMap;
	protected int documentSize;

	public DocumentFrequency(Documents documents) {
		idfMap = new TreeMap<String, Double>();
		documentSize = documents.getDocuments().size();
		generateMap(documents);
	}	
	
	public DocumentFrequency(File file) {
		idfMap = new TreeMap<String, Double>();
		generateMapFromFile(file);
	}

	public TreeMap<String, Double> getTfIdfMap() {
		return idfMap;
	}
	
	public void generateStatsDocument(String filePath) {
		Writer writer = null;
		try {
			writer = new BufferedWriter(
					new OutputStreamWriter(new FileOutputStream(filePath), "utf-8"));
			Iterator<Entry<String, Double>> it = idfMap.entrySet().iterator();
			while (it.hasNext()) {
				Entry<String, Double> pair = it.next();
				writer.write(pair.getKey()+"\n");
				writer.write(pair.getValue()+"\n");
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				writer.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
	
	public static double calculateIdf(int df, int numberOfDocuments) {
		double temp = (double) numberOfDocuments / (double) df;
		return Math.log10(temp);
	}
	
	public static double calculateTFIdf(int tf, double idf) {
		return (double)tf*idf;
	}
	
	public boolean isScoreAboveThreshold(double score, double threshold) {
			if(score > threshold) {
				return true;
			}
		return false;
	}
	
	public void generateMapFromFile(File file) {
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(file));
			String keyLine;
			double value;
			while ((keyLine = reader.readLine()) != null) {
				value = Double.parseDouble(reader.readLine());
				idfMap.put(keyLine, value);
			}
		} catch (IOException e) {
			System.err.println("File not found");
			e.printStackTrace();
		} catch (NullPointerException | NumberFormatException e2) {
			System.err.println("Invalid file structure");
			e2.printStackTrace();
		} finally {
			try {
				reader.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
	
	public void printMap() {
		Iterator<Entry<String, Double>> it = idfMap.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, Double> pair = it.next();
			System.out.println(pair.toString());
		}
	}
	
	public boolean isScoreAboveThreshold(String string, int score, double threshold) {
		if(getStringTFIDFScore(string,score) > threshold) {
			return true;
		}
	return false;
}
	
	public double getStringTFIDFScore(String string, int tf) {
		if(idfMap.containsKey(string)) {
			double idf = idfMap.get(string);
			return calculateTFIdf(tf, idf);
					
		}
		else {
			throw new IllegalArgumentException("Invalid input");
		}
	}
	
	protected void insert(String s, TreeMap<String, Integer> dfMap) {
		if (dfMap.containsKey(s)) {
			dfMap.put(s, dfMap.get(s) + 1);
		} else {
			dfMap.put(s, 1);
		}
	}
	
	protected void generateTfIdfMap(TreeMap<String, Integer> tfMap) {
		Iterator<Entry<String, Integer>> it = tfMap.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, Integer> pair = it.next();
			idfMap.put(
					pair.getKey(),
					calculateIdf((int) pair.getValue(), documentSize));
		}
	}
	
	protected double getTFIDFScore(IDocumentUnits input) {
		String inputLowerCase = input.getStripped().toLowerCase();
		if(idfMap.containsKey(inputLowerCase)) {
			int tf = input.getCountManager().getMap().get(inputLowerCase);
			return getStringTFIDFScore(inputLowerCase, tf);					
		}
		else {
			throw new IllegalArgumentException("Invalid input");
		}
	}
	
	protected double getIDFScore(IDocumentUnits input) {
		String inputLowerCase = input.getStripped().toLowerCase();
		if(idfMap.containsKey(inputLowerCase)) {
			return idfMap.get(inputLowerCase);				
		}
		else {
			throw new IllegalArgumentException("Invalid input");
		}
	}
	
	public boolean isAboveTFIDFThreshold(IDocumentUnits input, double threshold) {
		return isScoreAboveThreshold(getTFIDFScore(input), threshold);
	}
	
	public boolean isAboveIDFThreshold(IDocumentUnits input, double threshold) {
		System.out.println(input.getStripped()+ ": "+getIDFScore(input));
		return isScoreAboveThreshold(getIDFScore(input), threshold);
	}
	

	protected abstract void generateMap(Documents documents);	
	
}
